package org.xbl.xchain.sdk.crypto.encode;

import com.google.common.primitives.Bytes;
import org.bitcoinj.core.AddressFormatException;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

public class ConvertBits {

    /**
     * go版本还原bit转换工具
     */
    //编码转换工具
    public static byte[] convertBitsFromOrign(byte[] data, byte fromBits,byte toBits,boolean pad){
        if(fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8) {
            return null;
        }
        List<Byte> regroupedList = new ArrayList<>();
        byte[] regrouped;
        byte nextByte = (byte)0;
        byte filledBits = (byte)0;
        for(Byte b:data){
            b = (byte)((b & 0xff )<< (8-fromBits));
            byte remFromBits = fromBits;
            while(remFromBits>0){
                byte remToBits = (byte)(toBits - filledBits);
                byte toExtract = remFromBits;
                if(remToBits<toExtract) {
                    toExtract = remToBits;
                }
                    byte nextByte1 = (byte)((nextByte & 0xff) << toExtract);
                    byte nextByte2 = (byte)((b & 0xff ) >>> (8-toExtract));
                    nextByte = (byte)(nextByte1 | nextByte2);
                    b = (byte)((b & 0xff )<< toExtract);
                    remFromBits -= toExtract;
                    filledBits += toExtract;
                    if(filledBits==toBits){
                        regroupedList.add(nextByte);
                        filledBits = (byte) 0;
                        nextByte = (byte) 0;
                    }

            }

        }
        if(pad&&filledBits>0){
            nextByte = (byte)((nextByte & 0xff )<< (toBits-filledBits));
            regroupedList.add(nextByte);
            filledBits=(byte)0;
            nextByte=(byte)0;
        }
        if(filledBits>0&&(filledBits>4||nextByte!=0)){
            return null;
        }
        regrouped = new byte[regroupedList.size()];
        //将regroupedList转换为regrouped数组
        for(int i=0;i<regrouped.length;i++){
            regrouped[i]=regroupedList.get(i);
        }
        return regrouped;
    }

    /**
     * Helper for re-arranging bits into groups.
     */
    public static byte[] convertBits(final byte[] in, final int inStart, final int inLen, final int fromBits,
                                      final int toBits, final boolean pad) throws AddressFormatException {
        int acc = 0;
        int bits = 0;
        ByteArrayOutputStream out = new ByteArrayOutputStream(64);
        final int maxv = (1 << toBits) - 1;
        final int max_acc = (1 << (fromBits + toBits - 1)) - 1;
        for (int i = 0; i < inLen; i++) {
            int value = in[i + inStart] & 0xff;
            if ((value >>> fromBits) != 0) {
                throw new AddressFormatException(
                    String.format("Input value '%X' exceeds '%d' bit size", value, fromBits));
            }
            acc = ((acc << fromBits) | value) & max_acc;
            bits += fromBits;
            while (bits >= toBits) {
                bits -= toBits;
                out.write((acc >>> bits) & maxv);
            }
        }
        if (pad) {
            if (bits > 0)
                out.write((acc << (toBits - bits)) & maxv);
        } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) {
            throw new AddressFormatException("Could not convert bits, invalid padding");
        }
        return out.toByteArray();
    }

    /**
     * @Description: 相当于convertBits反推，注意fromBits和toBits
     * @Param:
     * @return:
     * @Date: 2019/3/11
     */
    public static byte[] convertBitsEx(List<Byte> data, int fromBits, int toBits, boolean pad) throws Exception    {

        int acc = 0;
        int bits = 0;
        int maxv = (1 << toBits) - 1;
        List<Byte> ret = new ArrayList<Byte>();

        for(Byte value : data)  {

            short b = (short)(value.byteValue() & 0xff);

            if (b < 0) {
                throw new Exception();
            }
            else if ((b >> fromBits) > 0) {
                throw new Exception();
            }
            else    {
                ;
            }

            acc = (acc << fromBits) | b;
            bits += fromBits;
            while (bits >= toBits)  {
                bits -= toBits;
                ret.add((byte)((acc >> bits) & maxv));
            }
        }

        if(pad && (bits > 0))    {
            ret.add((byte)((acc << (toBits - bits)) & maxv));
        }
        else if (bits >= fromBits || (byte)(((acc << (toBits - bits)) & maxv)) != 0)    {
            throw new Exception("panic");
        }
        else    {
            ;
        }

        return Bytes.toArray(ret);
    }
}
